/*
 * TCP服务器.c
 * 
 * 
 */

/*
 * -------------------------------------------------------------------
 * 
 * 头文件
 *-------------------------------------------------------------------
 */
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <signal.h>
#include <time.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>
/*
 * -------------------------------------------------------------------
 * 
 * 宏定义
 *-------------------------------------------------------------------
 */
#define NAME_SIZE 20
#define CONT_SIZE 128
/*
 * -------------------------------------------------------------------
 * 
 * 结构体
 *-------------------------------------------------------------------
 */
enum Chat_Opt{
	ADD,
	DEL,
	CHAT,
	ONLINE
};

typedef struct Chat_pack{
//head
	char head[5];
//data
	enum Chat_Opt chat_opt;
	char sender[NAME_SIZE];
	char recv[NAME_SIZE];
	int checknum;
	char content[CONT_SIZE];
}chat_t;

typedef struct node{
	int client_fd;
	char cname[NAME_SIZE];
	struct node* next;
}client_t;
/*
 * -------------------------------------------------------------------
 * 
 * 全局变量
 *-------------------------------------------------------------------
 */
pthread_t th;
int sockfd;
client_t* head;
/*
 * -------------------------------------------------------------------
 * 
 * 函数声明
 *-------------------------------------------------------------------
 */
int find_fd(client_t *head, char recv[NAME_SIZE]);
void list_insert(client_t *head, int fd, char* cname);
void list_del(client_t *head, int fd);
/*
 * -------------------------------------------------------------------
 * 函数：sig_handler
 * 功能：登记信号处理函数，使程序接收ctrl+C信号时关闭服务器
 *-------------------------------------------------------------------
 */
void sig_handler(int signo)
{
	if(signo==SIGINT)
	{
		printf("server close\n");
		/*步骤6：关闭客户端的socket*/
		pthread_cancel(th);
		close(sockfd);
		exit(0);
	}
}
/*
 * -------------------------------------------------------------------
 * 函数：out_addr
 * 功能：输出连接上来的客户端相关信息
 *-------------------------------------------------------------------
 */
void out_addr(struct sockaddr_in *clientaddr)
{
	//将端口号从网络字节序转换成主机字节序
	int port = ntohs(clientaddr->sin_port);
	char ip[16];
	memset(ip,0,sizeof(ip));
	//将ip地址从网络字节序转换成点分十进制
	inet_ntop(AF_INET,
					&clientaddr->sin_addr.s_addr,
					ip, sizeof(ip));
	printf("client: %s(%d) connected\n",ip,port);
}

/*
 * -------------------------------------------------------------------
 * 线程函数
 * 
 *-------------------------------------------------------------------
 */
void* th_fn(void* arg)
{
	int fd = *(int*)arg;
	char buffer[512] ;
	memset(buffer,0,sizeof(buffer));
	chat_t chat;
	int cfd;
	char cmd[5]={'\0'};
	
	
	while(1){
		
		size_t size = read(fd,buffer,512);
		if(size == 0){	//客户端已关闭连接
			printf("client %s closed\n",chat.sender);
			list_del(head, fd);
			pthread_cancel(pthread_self());
			
		}else if(size>0){
			memcpy(cmd,buffer,5);
			if(!strcmp(cmd,"chat")){
				
				memset(&chat,0,sizeof(chat_t));
				memcpy(&chat,buffer,sizeof(chat_t));
				if(find_fd(head,chat.sender)<0)list_insert(head,fd,chat.sender);
				
				if((cfd=find_fd(head,chat.recv))>=0)write(cfd,buffer,512);
				else{
					//printf("在线错误。\n");
					strcpy(chat.head,"erro");
					memset(buffer,0,512);
					memcpy(buffer,&chat,sizeof(chat_t));
					write(fd,buffer,512);
				}
			}
		}
	}
}

/*
 * -------------------------------------------------------------------
 * 
 * 主函数
 *-------------------------------------------------------------------
 */
int main(int argc, char **argv)
{
	head=(client_t*)calloc(1,sizeof(client_t));
	head->client_fd=-1;
	head->next=NULL;
	
	if(argc<2)
	{
		printf("usage:%s port\n",argv[0]); //传递端口参数
		exit(1);
	}
	/*处理ctl+c信号，关闭服务器*/
	if(signal(SIGINT, sig_handler)==SIG_ERR) 
	{
		perror("signal sigint error");
		exit(1);
	}

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family = AF_INET; //IPv4
	serveraddr.sin_port = htons(atoi(argv[1])); //port
	serveraddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY响应所有客户端请求
	if(bind(sockfd, (struct sockaddr*)&serveraddr,
				sizeof(serveraddr))<0)
		{perror("bind error");exit(1);}
		
	if(listen(sockfd, 10)<0)
	{perror("listen error");exit(1);}

	struct sockaddr_in clientaddr;
	socklen_t clientaddr_len = sizeof(clientaddr);
	//循环和多个客户端进行连接
	while(1){
		int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
		if(fd<0){
			perror("accept error");
			continue;
		}
		out_addr(&clientaddr);
		pthread_create(&th,0,th_fn,(void*)&fd);
		pthread_detach(th);
	}
	
	return 0;
}

/*
 * -------------------------------------------------------------------
 * 函数：find_fd
 * 功能：登记信号处理函数，使程序接收ctrl+C信号时关闭服务器
 *-------------------------------------------------------------------
 */
int find_fd(client_t *head, char recv[NAME_SIZE])
{
	client_t *p=head;
	while((p=p->next))
	{if(!strcmp(p->cname,recv))return p->client_fd;}
	return -1;
}
/*
 * -------------------------------------------------------------------
 * 函数：list_insert
 * 功能：登记信号处理函数，使程序接收ctrl+C信号时关闭服务器
 *-------------------------------------------------------------------
 */
void list_insert(client_t *head, int fd, char* cname)
{
	client_t *newnode=(client_t *)calloc(1,sizeof(client_t));
	newnode->client_fd=fd;
	strcpy(newnode->cname,cname);
	newnode->next=head->next;
	head->next=newnode;
}
/*
 * -------------------------------------------------------------------
 * 函数：list_del
 * 功能：登记信号处理函数，使程序接收ctrl+C信号时关闭服务器
 *-------------------------------------------------------------------
 */
void list_del(client_t *head, int fd)
{
	client_t *p=head;
	while(p->next){
		if(p->next->client_fd == fd){
			client_t *q=p->next;
			p->next=q->next;
			free(q);
			break;
		}
		p=p->next;
	}
}
